home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
...taking it to the Macs!
/
...taking it to the Macs!.iso
/
Extras
/
ActiveX Mac SDK
/
ActiveX SDK
/
Sample Controls
/
Label
/
CLabelControl.cp
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Text File
|
1996-12-20
|
55.2 KB
|
1,551 lines
|
[
TEXT/CWIE
]
#include "ocheaders.h"
#include "BDDISPIDs.h"
#include "CBaseControl.h"
#include "CErrorControl.h"
#include "CLabelControl.h"
#include "BDConsts.h"
#include "Colors.h"
#include "FnAssert.h"
#include "dispatch.h"
#include <LArray.h>
#include "CConnectionPoint.h"
#include "CCPContainer.h"
#include "CError.h"
#include "CLabelError.h"
#include "BDUtils.h"
#include "rotate.h"
#include <ctype.h>
#include <math.h>
#include <stdio.h>
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::CLabelControl
//
// Constructor
//
CLabelControl::CLabelControl(void) : CBaseControl()
{
const Rect nullRect = { 0, 0, 0, 0 };
// protected:
mMainOffscreenGWorld = NULL;
mPixOffscreenPixMap = NULL;
mRectOffscreenBounds = nullRect;
// user spec'd properties:
mAngle = 0;
mAlignment = 4;
mBackStyle = 1;
mBackColor = RGB_GRAY;
strcpy(mCaption, "Default String");
strcpy(mFontName, "Times");
mFontBold = 0;
mFontItalic = 0;
mFontUnderline = 0;
mFontStrikeout = 0;
mFontSize = 18;
mForeColor = RGB_BLACK;
mMode = 0;
mRotateBy = 0; // don't rotate when clicked, by default
strcpy((char*) mID[1], "Label");
mID[0] = strlen("Label");
CCPContainer* containerObj = nil;
// Create the new connection point container object
containerObj = new CCPContainer(NUM_CONNECTIONS);
// Snag the interface pointer
if (containerObj)
containerObj->QueryInterface(IID_IConnectionPointContainer, &mCPContainerP);
// if we have a container object, allocate the connection points
if (mCPContainerP)
// We support 1 connection point
containerObj->AddConnectionPoint(IID_IDoMenuEvents);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::~CLabelControl
//
// Destructor
//
CLabelControl::~CLabelControl(void)
{
// clean up offscreen world
if (mMainOffscreenGWorld != NULL)
{
::DisposeGWorld(mMainOffscreenGWorld);
mMainOffscreenGWorld = NULL;
mPixOffscreenPixMap = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::IUnknown::QueryInterface
//
// Returns a pointer to the specified interface on a component to which a
// client currently holds an interface pointer.
//
STDMETHODIMP
CLabelControl::QueryInterface(REFIID inRefID, void** outObj)
{
HRESULT resultCode = ResultFromScode(E_NOINTERFACE); // init it cause I'm anal
if (inRefID == IID_IBindStatusCallback)
{
resultCode = CBaseBindStatusCallback::QueryInterface(inRefID, outObj);
}
else if (inRefID == IID_IDoMenuEvents) // an outgoing interface
{
*outObj = (void*) (IDoMenuEvents*) this;
AddRef();
resultCode = ResultFromScode(S_OK);
}
else
{
resultCode = CBaseControl::QueryInterface(inRefID, outObj);
}
return resultCode;
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::IControl::Draw
//
// Context the drawing context for ActiveX - includes the port
//
// Draw the label object.
//
STDMETHODIMP
CLabelControl::Draw(THIS_ DrawContext* Context)
{
if (Context->DrawAspect != DVASPECT_CONTENT)
return ResultFromScode(DV_E_DVASPECT);
this->PrepareOffscreenWorld(Context); // get the main offscreen world setup and ready
// get the offscreen bitmap
BitMap* offscreenBitMap = this->GetOffscreenBitMap();
// set the user selectable font properties
this->SetFontParams(mMainOffscreenGWorld);
// draw the text in the label
this->RenderLabelControl();
// Move it
this->MoveOnScreen(offscreenBitMap, Context);
// unlock, and unload
this->ReleaseOffscreenBitMap(offscreenBitMap);
return ResultFromScode(S_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::IControl::DoMouse
//
STDMETHODIMP CLabelControl::DoMouse(THIS_ MouseEventType inMouseET, PlatformEvent* inEvent)
{
switch (inMouseET)
{
case MouseDown:
{
// If the user has set a rotateby parameter, then rotate when we click.
if ( mRotateBy != 0 )
{
// adjust the rotation angle -- we only handle (0 <= mRotateBy <= 360)
mAngle += mRotateBy;
mAngle %= fullRevolution; // Limit it to less than a revolution
// get the current drawing environment
DrawContext context = {(PortType) 0};
mContainerSiteP->AcquireContext(mActiveContext->GetContextID(), &context);
// draw
Draw(&context);
// reset the drawing environment
mContainerSiteP->ReleaseContext(&context);
}
break;
}
case MouseEnter:
{
FireEvent(IID_IDoMenuEvents, DISPID_POPUP, inEvent);
break;
}
case MouseUp:
// We don't do anything for a mouse up event, since we handle
// it with the mouse down.
break;
}
return ResultFromScode(S_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::IPersistPropertyBag::Load
//
// PropBag container that holds all the properties passed in
// ErrorLog ptr to an error log
//
// Sets the data members that represent the user definable properties from the URL.
//
STDMETHODIMP CLabelControl::Load(IPropertyBag* PropBag, IErrorLog* ErrorLog)
{
SCODE theSCode = LoadTextState(PropBag, ErrorLog);
return ResultFromScode(theSCode);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::IPersistPropertyBag::LoadTextState
//
// pPropertyBag container that holds all the properties passed in
// pErrorLog ptr to an error log
//
// Sets the data members that represent the user definable properties from the container stream.
//
STDMETHODIMP CLabelControl::LoadTextState(IPropertyBag *pPropertyBag,IErrorLog *pErrorLog)
{
const long maxLength = 256;
char propertyString[MAX_PROPERTY_STRING_LENGTH];
// try to load in each property. if we can't get it, then leave
// things at the default.
// The rotation angle
if (::LoadPropertyString(pPropertyBag, "angle", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
mAngle = atoi(propertyString);
mAngle %= fullRevolution; // limit to: 0 <= mAngle <= 360
// The caption alignment
if (::LoadPropertyString(pPropertyBag, "alignment", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
mAlignment = atoi(propertyString);
// The backgroung property
if (::LoadPropertyString(pPropertyBag, "backstyle", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
mBackStyle = atoi(propertyString);
// The background color
if (::LoadPropertyString(pPropertyBag, "backcolor", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
this->LoadColor(&mBackColor, propertyString);
// The caption string
if (::LoadPropertyString(pPropertyBag, "caption", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
strcpy(mCaption, propertyString);
// The fontname string
if (::LoadPropertyString(pPropertyBag, "fontname", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
strcpy(mFontName, propertyString);
// Is the caption font bold style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
if (::LoadPropertyString(pPropertyBag, "fontbold", propertyString, maxLength, pErrorLog))
mFontBold = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
// Is the caption font italic style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
if (::LoadPropertyString(pPropertyBag, "fontitalic", propertyString, maxLength, pErrorLog))
mFontItalic = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
// Is the caption font underline style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
if (::LoadPropertyString(pPropertyBag, "fontunderline", propertyString, maxLength, pErrorLog))
mFontUnderline = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
// Is the caption font strikeout style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
if (::LoadPropertyString(pPropertyBag, "fontstrikeout", propertyString, maxLength, pErrorLog))
mFontStrikeout = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
// The font size
if (::LoadPropertyString(pPropertyBag, "fontsize", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
mFontSize = atoi(propertyString);
// The foreground color
if (::LoadPropertyString(pPropertyBag, "forecolor", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
this->LoadColor(&mForeColor, propertyString);
// The text rendering mode
if (::LoadPropertyString(pPropertyBag, "mode", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
mMode = atoi(propertyString);
// Rotate on click mode.
if (::LoadPropertyString(pPropertyBag, "rotateby", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
mRotateBy = atoi(propertyString);
// The object name of the label
if (::LoadPropertyString(pPropertyBag, "sourceobjecttag_", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
{
strcpy((char*)(&mID[1]), propertyString);
mID[0] = strlen(propertyString);
}
return ResultFromScode(S_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::FireEvent
//
STDMETHODIMP
CLabelControl::FireEvent(REFIID RefID, long EventID, PlatformEvent* Event)
{
IEnumConnectionPoints* enumCP;
IEnumConnections* enumC;
CONNECTDATA connectData;
IUnknown* eventTarget;
IConnectionPoint* connectionPoint;
// Get an enumerator for the connection points
if (SUCCEEDED(mCPContainerP->EnumConnectionPoints(&enumCP)))
{
// Loop through all the connection points for this control
while (enumCP->Next(1, &connectionPoint, nil) == NOERROR)
{
// Get all the connections for this connection point
if (SUCCEEDED(connectionPoint->EnumConnections(&enumC)))
{
// Loop through all the connections for this connection point
while (enumC->Next(1, &connectData, nil) == NOERROR)
{
// Get the interface implementation for this connection
// if successful, fire the event
if (SUCCEEDED(connectData.pUnk->QueryInterface(RefID, (void**) &eventTarget)))
FireOneEvent(RefID, EventID, eventTarget, Event);
}
// Release the enumerator
enumC->Release();
}
}
enumCP->Release();
}
return ResultFromScode(S_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::CEventSender::FireOneEvent
//
STDMETHODIMP
CLabelControl::FireOneEvent(REFIID /*RefID*/, long EventID, IUnknown* EventTarget, PlatformEvent* Event)
{
IDoMenuEvents* popTarget = (IDoMenuEvents*) EventTarget;
IUnknown* unk;
this->QueryInterface(IID_IUnknown, (void**) &unk);
switch (EventID)
{
case DISPID_POPUP:
popTarget->Popup(unk, Event);
break;
}
return ResultFromScode(S_OK);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::LoadColor
//
//
// theColor an RGBColor that defines the color tuple exactly (i.e., not indexed)
// colorString a string with a color in it repesented in ASCII
//
// Sets the color data member based on the value of the user definable property.
//
Boolean CLabelControl::LoadColor(RGBColor * theColor, char * colorString)
{
Boolean didSet = true;
for (short i = 0; i < strlen(colorString); i++)
tolower(colorString[i]);
if (streq(colorString, "black"))
*theColor = RGB_BLACK;
else if (streq(colorString, "white"))
*theColor = RGB_WHITE;
else if (streq(colorString, "red"))
*theColor = RGB_RED;
else if (streq(colorString, "green"))
*theColor = RGB_GREEN;
else if (streq(colorString, "blue"))
*theColor = RGB_BLUE;
else if (streq(colorString, "cyan"))
*theColor = RGB_CYAN;
else if (streq(colorString, "magenta"))
*theColor = RGB_MAGENTA;
else if (streq(colorString, "yellow"))
*theColor = RGB_YELLOW;
else
{
didSet = false; // reverse Boolean logic -- false unless we succeed
// We're expecting a 32-bit hex RGB value in the format #00bbggrr.
//
// NOTE: To be compatible with Windows, if the high-order bit is set,
// the low-order byte is supposed to be treated as a system color index.
// We'll punt on this for now.
Boolean punt = false;
// sanity check on string format. #bbggrr, #0bbggrr, or #00bbggrr.
int len = strlen(colorString);
if ((colorString[0] == '#') && (len >= 7) && (len <= 9))
{
// if len == 9, the input may be trying to set the high-order
// bit. Check for this and punt if so.
if (len == 9)
{
char * sHi = "0x??";
short iHi = 0;
strncat(sHi, colorString, 2);
sscanf(sHi, "%hx", &iHi);
punt = iHi & 0x40;
}
if (! punt) // if we're NOT setting hi-order bit...
{
const int mostSignificantBits = 8; // amount to shift to move a byte
const int numComponents = 3; // number of array components - one ea for R,G & B
const int redPos = 2; // array position for component
const int greenPos = 1; // array position for component
const int bluePos = 0; // array position for component
unsigned short desiredColorArray[numComponents]; // a COLORREF, as an array
for (short i = 0, pos = len-2; i < numComponents; i++, pos -= 2)
{
char * sColor = "0x??";
short aShort = 0;
strncpy(&sColor[2], &colorString[pos], 2);
sscanf(sColor, "%hx", &aShort);
ASSERT((aShort <= 0xff), "Range check error!");
desiredColorArray[i] = aShort << mostSignificantBits;// move the color component value into the ms bits
}
// return the color that we went to all that trouble to set
theColor->red = (unsigned short) desiredColorArray[redPos];
theColor->green = (unsigned short) desiredColorArray[greenPos];
theColor->blue = (unsigned short) desiredColorArray[bluePos];
// success, so tell the world
didSet = true;
}
}
}
return didSet;
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::PrepareOffscreenWorld
//
// Context the drawing context for ActiveX - includes the port
//
// Creates the offscreen gworld that is the current display.
//
void CLabelControl::PrepareOffscreenWorld(const DrawContext* Context)
{
try
{
// get the current drawing environment
// If there's already an offscreen GWorld, get rid of it.
if (mMainOffscreenGWorld != nil)
{
DisposeGWorld(mMainOffscreenGWorld);
mMainOffscreenGWorld = nil;
}
// Define the size of the GWorld's bounding boxes -- same size as onscreen
short rightBounds = Context->Location.right - Context->Location.left;
short bottomBounds = Context->Location.bottom - Context->Location.top;
SetRect(&mRectOffscreenBounds, 0, 0, rightBounds, bottomBounds);
// Allocate a new GWorld for the offscreen drawing and store its PixMap.
GDHandle currDevice = ::GetGDevice();
short targetDepth = (**(**currDevice).gdPMap).pixelSize; // how many pixels deep are we?
QDErr err = ::NewGWorld(&mMainOffscreenGWorld, targetDepth, &mRectOffscreenBounds, 0, nil, 0);
if (!err)
{
mPixOffscreenPixMap = ::GetGWorldPixMap(mMainOffscreenGWorld);
// we should now have a gworld
if (mMainOffscreenGWorld == nil || mPixOffscreenPixMap == nil)
throw CLabelError(CONTROL_GWORLD_ALLOCATION_ERROR, this);
// Clear the image
this->ClearDisplay();
}
}
// error handling
catch (CLabelError &labelError)
{
labelError.HandleError();
}
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::ClearDisplay
//
// No params no parameters are passed or returned
//
// Erases the main offscreen world to the background color passed in the properties
//
void CLabelControl::ClearDisplay(void)
{
// Store the current port and device before switching to the offscreen world.
CGrafPtr currentPort;
GDHandle currentDevice;
::GetGWorld(¤tPort, ¤tDevice);
// Switch to the offscreen GWorld and lock the offscreen buffer in memory.
::SetGWorld(mMainOffscreenGWorld, nil);
// Set the background color before we erase
::RGBBackColor(&mBackColor);
// Erase the PixMap's bounding box in the GWorld.
::EraseRect(&mRectOffscreenBounds);
// restore the original port and device
::SetGWorld(currentPort, currentDevice);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::MoveOnScreen
//
// offscreenBitMap a PixMap or BitMap pointer
// Context the drawing context for ActiveX - includes the port
//
// Computes the size of the rect needed to hold the rotated rect.
//
void CLabelControl::MoveOnScreen(const BitMap* /*offscreenBitMap*/, const DrawContext* Context)
{
// Set the colors
::RGBForeColor(&RGB_BLACK);
::RGBBackColor(&RGB_WHITE);
// actually move the bits
::CopyBits((BitMap*)*mPixOffscreenPixMap, &(Context->Port->portBits), &mRectOffscreenBounds, &Context->Location, srcCopy, NULL);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::GetOffscreenBitMap
//
// returns BitMap a PixMap or BitMap pointer
//
// Returns a BitMap/PixMap that is part of the offscreen world - ready to draw into.
//
BitMap * CLabelControl::GetOffscreenBitMap(void)
{
// sanity check
ASSERT((mPixOffscreenPixMap != NULL), "NULL offscreen pixmap!");
// Lock n load the pixMap handle 'till we're done with it
::LockPixels(mPixOffscreenPixMap);
// lock handle so it does not float on us
::HLock((Handle)mPixOffscreenPixMap);
// Note the bitmap, now that we've locked n loaded
BitMap* offscreenBitMap = (BitMap *)(*mPixOffscreenPixMap);
ASSERT((offscreenBitMap != NULL), "NULL offscreen bitmap!");
return offscreenBitMap;
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::ReleaseOffscreenBitMap
//
// offscreenBitMap an offscreenBitMap that we are through with and would like to purge.
//
// Unlocks handles and pixels in preparation for disposing of a GWorld
//
void CLabelControl::ReleaseOffscreenBitMap(BitMap * offscreenBitMap)
{
// we are done with this handle so let it float
::HUnlock((Handle)mPixOffscreenPixMap);
// Release the pixMap handle
::UnlockPixels(mPixOffscreenPixMap);
// Set this to NULL just as a safety factor
offscreenBitMap = NULL;
}
///////////////////////////////////////////////////////////////////////////////
//
// P R I V A T E D R A W I N G S U P P O R T
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::SetFontParams
//
// theGWorld a GWorld that we want to set up the font parameters in
//
// This routine sets the GWorld font params up based on the properties that were passed.
//
void CLabelControl::SetFontParams(GWorldPtr theGWorld)
{
// switch to the offscreen GWorld to set up the font stuff
CGrafPtr currentPort;
GDHandle currentDevice;
::GetGWorld(¤tPort, ¤tDevice);
::SetGWorld(theGWorld, NULL);
// set the font info up from what was passed into us
if (mFontBold) TextFace(bold);
if (mFontItalic) TextFace(italic);
if (mFontUnderline) TextFace(underline);
// convert fontname to a pascal string
Str255 fontName;
strcpy((char *)fontName, mFontName);
c2pstr((char *)fontName);
short familyID = 0;
::GetFNum(fontName, &familyID);
if (familyID == 0)
::TextFont(applFont);
else
::TextFont(familyID);
::TextSize(this->mFontSize);
::TextMode(srcCopy);
::GetFontInfo(&mFontInfo); // save off the current font info
// Set the port and device back to the original
::SetGWorld(currentPort, currentDevice);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::GetTextSize
//
// aString a string of the pascal type
// textSize the size of a Rect expressed as the bottom-right corner
//
// Computes the size of a rect needed to hold aString (assumes that the GWorld
// has been init'd to the correct font and styles).
//
void CLabelControl::GetTextSize(const StringPtr aString, Point * textSize)
{
// Store the current port and device before switching to the offscreen world.
CGrafPtr currentPort;
GDHandle currentDevice;
::GetGWorld(¤tPort, ¤tDevice);
// Switch to the offscreen GWorld so that the string width is accurate
::SetGWorld(mMainOffscreenGWorld, NULL);
textSize->v = mFontInfo.ascent + mFontInfo.descent; // compute height
textSize->h = StringWidth(aString); // compute width
// restore the saved off port
::SetGWorld(currentPort, currentDevice);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::RenderLabelControl
//
// No params no parameters are passed or returned
//
// Actually does the drawing of the label. It creates the content GWorld. Then
// rotates it as needed. Then blits it into the offscreen PixMap (data member).
//
void CLabelControl::RenderLabelControl(void)
{
Point contentSize = {0, 0}; // nil going in -- comes back to us with the size of the bitmap
GWorldPtr contentGWorld = nil; // init to nil to satisfy the compiler
GWorldPtr scratchGWorld = nil; // ditto
short rotationAngle;
CGrafPtr currentPort; // use for saving off curr GWorld
GDHandle currentDevice;
// build a GWorld just big enough for the string and then draw draw string into it
QDErr theErr = CreateContent(&contentSize, contentGWorld);
PixMapHandle contentPixMap = ::GetGWorldPixMap(contentGWorld);
if(mAngle > quarterRevolution) // 360 < angle < 90
{
short preRotate = mAngle / quarterRevolution; // how many times must we rotate by 90?
rotationAngle = mAngle % quarterRevolution; // reduce angle - e.g., how much more do we rotate?
switch(preRotate) // number of times to rotate by 90 degrees (i.e., 1 for 2nd quad, 2 for 3rd and 3 for 4th)
{
case 1:
{
GWorldPtr preRotateGWorld = nil; // init to nil to satisfy the compiler
theErr = Rotate90(contentGWorld, preRotateGWorld);
ASSERT(theErr == noErr, "failed to pre-rotate by 90");
// blow away previous content and recreate it
ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
::DisposeGWorld(contentGWorld); // we have rotated so now we can blow it away
contentGWorld = nil; // just to make double sure
// Recreate content GWorld:
Rect preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds; // how many pixels deep are we?
try
{
theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
ASSERT(theErr == noErr, "failed to recreate content GWorld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
// prepare to copy the pre-rotated version into the new content
PixMapHandle preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
::LockPixels(preRotatePixMap);
::HLock((Handle)preRotatePixMap);
contentPixMap = ::GetGWorldPixMap(contentGWorld);
::LockPixels(contentPixMap);
::HLock((Handle)contentPixMap);
::GetGWorld(¤tPort, ¤tDevice);
::SetGWorld(contentGWorld, nil); // Switch to the scratch GWorld
::RGBForeColor(&RGB_BLACK);
::RGBBackColor(&RGB_WHITE);
// now copy back into the (new) content
::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap),
&(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore GWorld
// reset the content size:
contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
::DisposeGWorld(preRotateGWorld);
// rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd
// by the rotation routine but we must dispose of it when completed drawing
GWorldPtr rotatedGWorld = nil; // init to nil to satisfy the compiler
theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)rotationAngle), mBackColor, rotatedGWorld);
ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
if(theErr != noErr)
throw CLabelError(ROTATION_FAILED_ERROR, this);
// now compute the alignment our content GWorld within the label GWorld
PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
Rect contentBounds = (**rotatedPixMap).bounds; // get a copy of the rotated rect
ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
// Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
// in the 256 color depth. Then we can copy the entire 256 color into the final
// GWorld at the target bitmap color depth.
theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
ASSERT(theErr == noErr, "failed to alloc scratch GWorrld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
::LockPixels(scratchPixMap);
::HLock((Handle)scratchPixMap);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(scratchGWorld, nil); // Set the port and device back to the original
::RGBBackColor(&mBackColor); // Set the background color before we erase
::EraseRect(&(**scratchPixMap).bounds); // Erase the PixMap's bounding box in the GWorld.
::RGBForeColor(&RGB_BLACK);
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap),
&(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
::DisposeGWorld(rotatedGWorld); // cleanup the rotation artifacts
::SetGWorld(currentPort, currentDevice); // restore to the previous GWorld
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(mMainOffscreenGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
// copy the content into the label
::RGBBackColor(&mBackColor); // Set the background color before we erase
::EraseRect(&(**mPixOffscreenPixMap).bounds); // Erase the PixMap's bounding box in the GWorld.
::RGBForeColor(&RGB_BLACK);
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap),
&(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore the port and device back to the original
::DisposeGWorld(scratchGWorld); // cleanup the scratch artifacts
::DisposeGWorld(contentGWorld);
}// try
// error handling
catch (CLabelError &labelError)
{
labelError.HandleError();
}
break;
}// case 1
case 2:
{
try
{
GWorldPtr preRotateGWorld = nil; // init to nil to satisfy the compiler
theErr = Rotate90(contentGWorld, preRotateGWorld);
ASSERT(theErr == noErr, "failed to pre-rotate by 180");
// blow away previous content and recreate it
ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
::DisposeGWorld(contentGWorld);
contentGWorld = nil; // just to make double sure
// Recreate content GWorld:
Rect preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds; // how many pixels deep are we?
theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
ASSERT(theErr == noErr, "failed to recreate content GWorld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
// prepare to copy the pre-rotated version into the new content
PixMapHandle preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
::LockPixels(preRotatePixMap);
::HLock((Handle)preRotatePixMap);
contentPixMap = ::GetGWorldPixMap(contentGWorld);
::LockPixels(contentPixMap);
::HLock((Handle)contentPixMap);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(contentGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
// now copy back into the (new) content
::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap),
&(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore to the previous GWorld
// reset the content size:
contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
::DisposeGWorld(preRotateGWorld);
preRotateGWorld = nil; // re-init to just to make sure
theErr = Rotate90(contentGWorld, preRotateGWorld);
ASSERT(theErr == noErr, "failed to pre-rotate by 180");
// now we copy it back again:
// blow away previous content and recreate it
ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
::DisposeGWorld(contentGWorld);
contentGWorld = nil; // just to make double sure
// Recreate content GWorld:
preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds; // how many pixels deep are we?
theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
ASSERT(theErr == noErr, "failed to recreate content GWorld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
// prepare to copy the pre-rotated version into the new content
preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
::LockPixels(preRotatePixMap);
::HLock((Handle)preRotatePixMap);
contentPixMap = ::GetGWorldPixMap(contentGWorld);
::LockPixels(contentPixMap);
::HLock((Handle)contentPixMap);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(contentGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
// now copy back into the (new) content
::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap),
&(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore to the previous GWorld
// reset the content size:
contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
::DisposeGWorld(preRotateGWorld);
preRotateGWorld = nil; // re-init to just to make sure
// rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd
// by the rotation routine but we must dispose of it when completed drawing
GWorldPtr rotatedGWorld = nil; // init to nil to satisfy the compiler
theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)rotationAngle), mBackColor, rotatedGWorld);
ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
if(theErr != noErr)
throw CLabelError(ROTATION_FAILED_ERROR, this);
// now compute the alignment our content GWorld within the label GWorld
PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
Rect contentBounds = (**rotatedPixMap).bounds; // get a copy of the rotated rect
ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
// Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
// in the 256 color depth. Then we can copy the entire 256 color into the final
// GWorld at the target bitmap color depth.
theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
ASSERT(theErr == noErr, "failed to alloc scratch GWorrld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
::LockPixels(scratchPixMap);
::HLock((Handle)scratchPixMap);
// Store the current port and device before switching to the offscreen world.
::GetGWorld(¤tPort, ¤tDevice);
::SetGWorld(scratchGWorld, nil); // Switch to the scratch GWorld and lock the offscreen buffer in memory.
::RGBBackColor(&mBackColor); // Set the background color before we erase
::EraseRect(&(**scratchPixMap).bounds); // Erase the PixMap's bounding box in the GWorld.
::RGBForeColor(&RGB_BLACK);
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap),
&(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // Set the port and device back to the original
::DisposeGWorld(rotatedGWorld); // cleanup the rotation artifacts
::SetGWorld(mMainOffscreenGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
// copy the content into the label
::RGBForeColor(&RGB_BLACK);
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap),
&(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // Set the port and device back to the original
::DisposeGWorld(scratchGWorld); // cleanup the scratch artifacts
::DisposeGWorld(contentGWorld);
}// try
// error handling
catch (CLabelError &labelError)
{
labelError.HandleError();
}
break;
}// case 2
case 3:
{
try
{
GWorldPtr preRotateGWorld = nil; // init to nil to satisfy the compiler
theErr = Rotate90(contentGWorld, preRotateGWorld); // rotate for 90
ASSERT(theErr == noErr, "failed to pre-rotate by 180");
// blow away previous content and recreate it
ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
::DisposeGWorld(contentGWorld);
contentGWorld = nil; // just to make double sure
// Recreate content GWorld:
Rect preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds; // how many pixels deep are we?
theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
ASSERT(theErr == noErr, "failed to recreate content GWorld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
// prepare to copy the pre-rotated version into the new content
PixMapHandle preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
::LockPixels(preRotatePixMap);
::HLock((Handle)preRotatePixMap);
contentPixMap = ::GetGWorldPixMap(contentGWorld);
::LockPixels(contentPixMap);
::HLock((Handle)contentPixMap);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(contentGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
// now copy back into the (new) content
::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap),
&(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore to the previous GWorld
// reset the content size:
contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
::DisposeGWorld(preRotateGWorld);
preRotateGWorld = nil; // re-init to just to make sure
theErr = Rotate90(contentGWorld, preRotateGWorld); // rotate 2nd time for 180
ASSERT(theErr == noErr, "failed to pre-rotate by 180");
// now we copy it back again:
// blow away previous content and recreate it
ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
::DisposeGWorld(contentGWorld);
contentGWorld = nil; // just to make double sure
// Recreate content GWorld:
preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds; // how many pixels deep are we?
theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
ASSERT(theErr == noErr, "failed to recreate content GWorld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
// prepare to copy the pre-rotated version into the new content
preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
::LockPixels(preRotatePixMap);
::HLock((Handle)preRotatePixMap);
contentPixMap = ::GetGWorldPixMap(contentGWorld);
::LockPixels(contentPixMap);
::HLock((Handle)contentPixMap);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(contentGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
// now copy back into the (new) content
::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap),
&(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore to the previous GWorld
// reset the content size:
contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
::DisposeGWorld(preRotateGWorld);
preRotateGWorld = nil; // re-init to just to make sure
theErr = Rotate90(contentGWorld, preRotateGWorld); // rotate third time for 270
ASSERT(theErr == noErr, "failed to pre-rotate by 270");
// now we copy it back again:
// blow away previous content and recreate it
ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
::DisposeGWorld(contentGWorld);
contentGWorld = nil; // just to make double sure
// Recreate content GWorld:
preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds; // how many pixels deep are we?
theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
ASSERT(theErr == noErr, "failed to recreate content GWorld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
// prepare to copy the pre-rotated version into the new content
preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
::LockPixels(preRotatePixMap);
::HLock((Handle)preRotatePixMap);
contentPixMap = ::GetGWorldPixMap(contentGWorld);
::LockPixels(contentPixMap);
::HLock((Handle)contentPixMap);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(contentGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
// now copy back into the (new) content
::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap),
&(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore to the previous GWorld
// reset the content size:
contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
::DisposeGWorld(preRotateGWorld);
preRotateGWorld = nil; // re-init to just to make sure
// rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd
// by the rotation routine but we must dispose of it when completed drawing
GWorldPtr rotatedGWorld = nil; // init to nil to satisfy the compiler
theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)rotationAngle), mBackColor, rotatedGWorld);
ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
if(theErr != noErr)
throw CLabelError(ROTATION_FAILED_ERROR, this);
// now compute the alignment our content GWorld within the label GWorld
PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
Rect contentBounds = (**rotatedPixMap).bounds; // get a copy of the rotated rect
ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
// Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
// in the 256 color depth. Then we can copy the entire 256 color into the final
// GWorld at the target bitmap color depth.
theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
ASSERT(theErr == noErr, "failed to alloc scratch GWorrld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
::LockPixels(scratchPixMap);
::HLock((Handle)scratchPixMap);
// Store the current port and device before switching to the offscreen world.
::GetGWorld(¤tPort, ¤tDevice);
::SetGWorld(scratchGWorld, nil); // Switch to the scratch GWorld
::RGBBackColor(&mBackColor); // Set the background color before we erase
::EraseRect(&(**scratchPixMap).bounds); // Erase the PixMap's bounding box in the GWorld.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap),
&(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice);// restore to the previous GWorld
::DisposeGWorld(rotatedGWorld); // cleanup the rotation artifacts
::SetGWorld(mMainOffscreenGWorld, nil); // Switch to the offscreen GWorld.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap),
&(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice);// Set the port and device back to the original
::DisposeGWorld(scratchGWorld); // cleanup the scratch artifacts
::DisposeGWorld(contentGWorld);
}// try
// error handling
catch (CLabelError &labelError)
{
labelError.HandleError();
}
break;
}// case 3
}// switch(preRotate)
}// if (mAngle > quarterRevolution)
else
{
rotationAngle = mAngle; // did not have to pre-rotate so set to parameter
}// else
if(mAngle != 0 && mAngle <= quarterRevolution) // 0 < angle <= 90
{
try
{
// rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd
// by the rotation routine but we must dispose of it when completed drawing
GWorldPtr rotatedGWorld = nil; // init to nil to satisfy the compiler
theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)mAngle), mBackColor, rotatedGWorld);
ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
if(theErr != noErr)
throw CLabelError(ROTATION_FAILED_ERROR, this);
// now compute the alignment our content GWorld within the label GWorld
PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
Rect contentBounds = (**rotatedPixMap).bounds; // get a copy of the rotated rect
ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
// Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
// in the 256 color depth. Then we can copy the entire 256 color into the final
// GWorld at the target bitmap color depth.
theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
ASSERT(theErr == noErr, "failed to alloc scratch GWorld");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
::LockPixels(scratchPixMap);
::HLock((Handle)scratchPixMap);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(scratchGWorld, nil); // Switch to the offscreen GWorld
::RGBBackColor(&mBackColor); // Set the background color before we erase
::EraseRect(&((**scratchPixMap).bounds)); // Erase the PixMap's bounding box in the GWorld.
::RGBForeColor(&RGB_BLACK); // set the first and last pallette colors before we copy
::RGBBackColor(&RGB_WHITE);
// copy the content into a 256 color version of the label
::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap),
&(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
::DisposeGWorld(rotatedGWorld); // cleanup the rotation artifacts
::SetGWorld(mMainOffscreenGWorld, nil); // Switch to the offscreen GWorld
::RGBBackColor(&mBackColor); // Set the background color before we erase
::EraseRect(&mRectOffscreenBounds); // Erase the PixMap's bounding box in the GWorld.
::RGBForeColor(&RGB_BLACK); // set the first and last palette colors before we copy
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap),
&(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
::DisposeGWorld(scratchGWorld); // cleanup the scratch artifacts
::SetGWorld(currentPort, currentDevice); // Set the port and device back to the original
}// try
// error handling
catch (CLabelError &labelError)
{
labelError.HandleError();
}
}// if(mAngle != 0 && mAngle <= 90)
else if(mAngle == 0) // angle == 0 so we are not rotating -- just copy content into label
{
// now compute the alignment our content GWorld within the label GWorld
Rect contentBounds = (**contentPixMap).bounds; // get a copy of the content rect
ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
::GetGWorld(¤tPort, ¤tDevice); // Store the current port and device before switching to the offscreen world.
::SetGWorld(mMainOffscreenGWorld, nil); // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
// Set the colors before we do a copybits so
::RGBForeColor(&RGB_BLACK);
::RGBBackColor(&RGB_WHITE);
::CopyBits((BitMap *)(*contentPixMap), (BitMap *)(*mPixOffscreenPixMap),
&(**contentPixMap).bounds, &contentBounds, srcCopy, NULL);
::SetGWorld(currentPort, currentDevice); // restore to the previous GWorld
}// else if(rotationAngle == 0)
// DisposeGWorld(contentGWorld);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::ComputeAlignmentRect
//
// theContentRect the rect that is to be aligned
// theLabelRect the parent rect (i.e., this one is stationary -- the other one moves)
//
// This routine aligns the contentRect inside theLabelRect. It does so
// according to the parameter passed as mAlignment by the OCX. This operation
// takes place on a rect that has been prepared and is ready to blit into
// place (i.e., it is rotated already).
//
void CLabelControl::ComputeAlignmentRect(Rect &theContentRect, const Rect theLabelRect)
{
// compute the differences between rect sizes:
short deltaVert = (theLabelRect.bottom - theLabelRect.top) - theContentRect.bottom;
short deltaHoriz = (theLabelRect.right - theLabelRect.left) - theContentRect.right;
// now compute the amount that we will offset the content rect by:
Point offsetAmount;
// do the horizontal coord first
switch(mAlignment)
{
case 0:
case 3:
case 6: // left aligned
offsetAmount.h = 0; // don't offset horiz
break;
case 1:
case 4:
case 7: // centered horizontally
offsetAmount.h = deltaHoriz / 2; // offset by half the difference horiz
break;
case 2:
case 5:
case 8: // right aligned
offsetAmount.h = deltaHoriz; // offset by the entire horiz difference
break;
default:
throw CLabelError(CONTROL_BAD_ALIGNMENT_PARAM_ERROR, this);
}
// now the vertical coord
switch(mAlignment)
{
case 0:
case 1:
case 2: // top aligned
offsetAmount.v = 0; // don't offset vert
break;
case 3:
case 4:
case 5: // centered vertically
offsetAmount.v = deltaVert / 2; // offset by half the difference vert
break;
case 6:
case 7:
case 8: // bottom aligned
offsetAmount.v = deltaVert; // offset by the entire vert difference
break;
default:
throw CLabelError(CONTROL_BAD_ALIGNMENT_PARAM_ERROR, this);
}
// now we can just offset the content rect:
OffsetRect(&theContentRect, offsetAmount.h, offsetAmount.v);
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::RotatedSize
//
// angDegrees the CCW angle in degrees from the horizontal
// origSize the size of a Rect expressed as the bottom-right corner
//
// Computes the size of the rect needed to hold the rotated rect.
//
Point CLabelControl::RotatedSize(const double angDegrees, const Point origSize)
{
double angRadians = DegressToRadians(angDegrees);
double compAngRadians = DegressToRadians(quarterRevolution - angDegrees);
Point rotSize = { sin(angRadians) * origSize.h + cos(angRadians) * origSize.v, // v = sin(ø)w + cos(ø)h
sin(compAngRadians) * origSize.h + cos(compAngRadians) * origSize.v }; // h same with angle = 90 - ø
return rotSize;
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::CreateContent
//
// textSize the size of a Rect that holds the caption expressed as
// the bottom-right corner
// contentGWorld a GWorld containing the caption (not rotated)
//
// Given a nil pointer to a GWorld return a GWorld setup with all the right params
// and with the caption string drawn in it.
//
QDErr CLabelControl::CreateContent(Point* textSize, GWorldPtr &contentGWorld)
{
QDErr theErr;
try
{
// Get the size of the Caption
Point sizeCaption = {0, 0}; // nil point cause I'm anal
Str255 strCaption; // temp holder for our string - we store it as a c-string
strcpy((char *)strCaption, mCaption);
c2pstr((char *)strCaption);
this->GetTextSize(strCaption, &sizeCaption); // get the smallest rect to hold our text
textSize->h = sizeCaption.h; // pass this back so we can use it later
textSize->v = sizeCaption.v;
const Rect contentBounds = { 0, 0, textSize->v, textSize->h };
// alloc the output GWorld and save results of failure
theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth , &contentBounds, 0, nil, 0);
ASSERT(theErr == noErr, "alloc contentGWorld failed!");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
this->SetFontParams(contentGWorld);
// Store the current port and device before switching to the offscreen world.
CGrafPtr currentPort;
GDHandle currentDevice;
::GetGWorld(¤tPort, ¤tDevice);
// Switch to the offscreen GWorld and lock the offscreen buffer in memory.
::SetGWorld(contentGWorld, NULL);
PixMapHandle contentPixMap = GetGWorldPixMap(contentGWorld);
::LockPixels(contentPixMap);
// Draw the text into the offsceen world...
// generate Pascal strings for drawing
Str255 captionStringToDraw;
strcpy((char *)captionStringToDraw, mCaption);
c2pstr((char *)captionStringToDraw);
// Set colors
::RGBBackColor(&mBackColor);
::RGBForeColor(&mForeColor);
::MoveTo(0, mFontInfo.ascent);
::DrawString(captionStringToDraw);
// After drawing the string, unlock the offscreen buffer in memory
// and set the port and device back to the original
::UnlockPixels(contentPixMap);
::SetGWorld(currentPort, currentDevice);
}// try
// error handling
catch (CLabelError &labelError)
{
labelError.HandleError();
}
return theErr; // pass back the error condition -- should only fail if low mem
}
///////////////////////////////////////////////////////////////////////////////
//
// CLabelControl::Rotate90
//
// inputGWorld a GWorld containing a bitmap to be rotated by 90 degrees CCW
// rotate90GWorld a GWorld containing the results of the 90 degree rotation
//
// Provided an inputGworld this routine rotates it 90 degrees CCW. A pointer
// to an output GWorld is provided as input. This GWorld is alloc'd by the
// rotation routine and returned.
//
QDErr CLabelControl::Rotate90(const GWorldPtr inputGWorld, GWorldPtr &rotate90GWorld)
{
try
{
// create the output buffer (--> rotateGWorld):
PixMapHandle inputPixMap = GetGWorldPixMap(inputGWorld); // get the PixMap from our unrotated GWorld
::LockPixels(inputPixMap);
const Rect inputBounds = (**inputPixMap).bounds; // get the bounds of the unrotated GWorld
// create bounds of rotated GWorld by swapping right and bottom coords for rect @ 0,0
const Rect rotate90Bounds = { 0, 0, inputBounds.right, inputBounds.bottom };
QDErr theErr = ::NewGWorld(&rotate90GWorld, twoHundredFiftySixColorsDepth , &rotate90Bounds, 0, nil, 0);
ASSERT(theErr == noErr, "alloc rotate90GWorld failed!");
if(theErr != noErr)
throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
if (theErr != noErr) return theErr; // bail and return the err code
PixMapHandle rotate90PixMap = GetGWorldPixMap(rotate90GWorld); // get the PixMap from our rotated GWorld
::LockPixels(rotate90PixMap);
::HLock((Handle)rotate90PixMap);
// now get the rowbytes for each bitmap since we need these to compute our addresses
short inputRowBytes = GetRowBytes(inputPixMap);
short rotate90RowBytes = GetRowBytes(rotate90PixMap);
// walk each pixel in the source PixMap and copy it to the rotated PixMap
Ptr inputPixMapBaseAddr = GetPixBaseAddr(inputPixMap); // always get the addr via this function
Ptr rotate90PixMapBaseAddr = GetPixBaseAddr(rotate90PixMap); // always get the addr via this function
unsigned char * src; // pointer to the begin of a src row (in scanline order)
unsigned char * dst; // pointer to the begin of a dst row (in scanline order)
unsigned char * curColumn = nil;
for (int row = 0; row < inputBounds.bottom; row++) // do all the rows
{
src = (unsigned char *)(inputPixMapBaseAddr + (inputRowBytes * row)); // init to begininning of current row
curColumn = (unsigned char *)(rotate90PixMapBaseAddr + row);// init to point to first pixel of first row
for (int column = 0; column < inputBounds.right; column++)// do all the columns
{
dst = curColumn + ((inputBounds.right - column - 1) * rotate90RowBytes);
*dst = src[column];
}// columns
}// rows
::UnlockPixels(inputPixMap);
}// try
// error handling
catch (CLabelError &labelError)
{
labelError.HandleError();
}
return noErr;
}
// end-of-file ////////////////////////////////////////////////////////////////